﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;

namespace NetTopologySuite.Index.Quadtree
{
	[Serializable]
	public class Quadtree<T> : ISpatialIndex<T>
	{
		public static Envelope EnsureExtent(Envelope itemEnv, double minExtent)
		{
			double num = itemEnv.MinX;
			double num2 = itemEnv.MaxX;
			double num3 = itemEnv.MinY;
			double num4 = itemEnv.MaxY;
			if (num != num2 && num3 != num4)
			{
				return itemEnv;
			}
			if (num == num2)
			{
				num -= minExtent / 2.0;
				num2 = num + minExtent / 2.0;
			}
			if (num3 == num4)
			{
				num3 -= minExtent / 2.0;
				num4 = num3 + minExtent / 2.0;
			}
			return new Envelope(num, num2, num3, num4);
		}

		public Quadtree()
		{
			this._root = new Root<T>();
		}

		public int Depth
		{
			get
			{
				if (this._root != null)
				{
					return this._root.Depth;
				}
				return 0;
			}
		}

		public bool IsEmpty
		{
			get
			{
				return this._root == null;
			}
		}

		public int Count
		{
			get
			{
				if (this._root != null)
				{
					return this._root.Count;
				}
				return 0;
			}
		}

		public void Insert(Envelope itemEnv, T item)
		{
			this.CollectStats(itemEnv);
			Envelope itemEnv2 = Quadtree<T>.EnsureExtent(itemEnv, this._minExtent);
			this._root.Insert(itemEnv2, item);
		}

		public bool Remove(Envelope itemEnv, T item)
		{
			Envelope itemEnv2 = Quadtree<T>.EnsureExtent(itemEnv, this._minExtent);
			return this._root.Remove(itemEnv2, item);
		}

		public IList<T> Query(Envelope searchEnv)
		{
			ArrayListVisitor<T> arrayListVisitor = new ArrayListVisitor<T>();
			this.Query(searchEnv, arrayListVisitor);
			return arrayListVisitor.Items;
		}

		public void Query(Envelope searchEnv, IItemVisitor<T> visitor)
		{
			this._root.Visit(searchEnv, visitor);
		}

		public IList<T> QueryAll()
		{
			IList<T> result = new List<T>();
			this._root.AddAllItems(ref result);
			return result;
		}

		private void CollectStats(Envelope itemEnv)
		{
			double width = itemEnv.Width;
			if (width < this._minExtent && width > 0.0)
			{
				this._minExtent = width;
			}
			double height = itemEnv.Height;
			if (height < this._minExtent && height > 0.0)
			{
				this._minExtent = height;
			}
		}

		private readonly Root<T> _root;

		private double _minExtent = 1.0;
	}
}
